
Public Class JoinDesigner

    Protected _Owner As Metis.Core.Functions.MetisResultFunction
    Protected WithEvents _JoinList As Metis.Core.Functions.MetisJoinList
    'Protected _TableList As DAMfunctionList
    Protected _SelectJoinOnClick As Boolean = True
    Protected _SelectedJoin As Metis.Core.Functions.MetisJoin
    Protected _TableContextMenuStrip As ContextMenuStrip

    Private _JoinLineOfs As Integer = 12
    Private _ClickPos As Drawing.Point
    Private _ClickButton As MouseButtons


    Public Event JoinClick(ByVal Sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    Public Event JoinRightClick(ByVal Sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    Public Event JoinDoubleClick(ByVal Sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    Public Event JoinCreated(ByVal Sender As Object, ByVal e As JoinDesignerJoinEventArgs)
    ''' <summary>Raised after a new Table was added to the Join-Designer</summary>
    Public Event TableCreated(ByVal Sender As JoinDesigner, ByVal e As JoinDesignerTableEventArgs)
    ''' <summary>Raised after a new Table was removed from the Join-Designer</summary>
    Public Event TableRemoved(ByVal Sender As JoinDesigner, ByVal e As JoinDesignerTableEventArgs)
    ''' <summary>Raised when a Table is beeing moved within the Join-Designer</summary>
    Public Event TableMoving(ByVal Sender As JoinDesigner, ByVal e As JoinDesignerTableEventArgs)
    ''' <summary>Raised after a Table was moved within the Join-Designer</summary>
    Public Event TableMoved(ByVal Sender As JoinDesigner, ByVal e As JoinDesignerTableEventArgs)


    Private Sub OnJoinCreatedHandler(ByVal Sender As Object, ByVal e As JoinDesignerJoinEventArgs)
        For Each j As Metis.Core.Functions.MetisJoin In _Owner.Joins
            If (e.LeftJoinField.Equals(j.LeftField) And e.RightJoinField.Equals(j.RightField)) OrElse _
               (e.LeftJoinField.Equals(j.RightField) And e.RightJoinField.Equals(j.LeftField)) Then
                Exit Sub
            End If
        Next

        Dim tmpJoin As New Metis.Core.Functions.MetisJoin(e.LeftJoinField, e.RightJoinField, e.JoinType, _Owner)
        _Owner.Joins.Add(tmpJoin)
        RaiseEvent JoinCreated(tmpJoin, e)
        Me.Refresh()
    End Sub

    Private Sub OnTableMovingHandler(ByVal e As JoinDesignerTableEventArgs)
        RaiseEvent TableMoving(Me, e)
    End Sub

    Private Sub OnTableMovedHandler(ByVal e As JoinDesignerTableEventArgs)
        RaiseEvent TableMoved(Me, e)
    End Sub

    Sub New()
        InitializeComponent()
    End Sub

    Sub Init(ByVal Owner As Metis.Core.Functions.MetisResultFunction)
        _Owner = Owner
        _JoinList = _Owner.Joins
        '_TableList = _Owner.References
        Clear()
        For Each item As Metis.Core.Functions.MetisRefFunction In _Owner.FunctionReferences
            AddTable(item)
        Next
        'For Each item As DAMfunctionJoin In _Owner.Joins
        '    AddJoin(item.LeftField, item.RightField, item.JoinType)
        'Next
    End Sub

    Sub Init(ByVal Owner As Metis.Core.Functions.MetisResultFunction, ByVal DropDownMenu As ContextMenuStrip)
        _TableContextMenuStrip = DropDownMenu
        Init(Owner)
    End Sub


    Function TableByObject(ByVal RepresentedObject As Metis.Core.Functions.MetisRefFunction) As JoinDesignerTable
        For Each item As Control In Me.Controls
            If TypeOf item Is JoinDesignerTable Then
                If DirectCast(item, JoinDesignerTable).Represents.Equals(RepresentedObject) Then Return DirectCast(item, JoinDesignerTable)
            End If
        Next
        Return Nothing
    End Function


    Function AddTable(ByVal Table As Metis.Core.Functions.MetisRefFunction, ByVal Location As Drawing.Point) As Boolean
        Dim tmpJDtable As JoinDesignerTable = TableByObject(Table)

        If tmpJDtable Is Nothing Then
            tmpJDtable = New JoinDesignerTable(Table, Me)
            For Each field As Metis.Core.Functions.MetisFunctionField In Table.ReferencedFunction.Fields
                tmpJDtable.Add(field)
            Next


            tmpJDtable.Location = Location
            tmpJDtable.ContextMenuStrip = _TableContextMenuStrip
            If _Owner.FunctionReferences.IndexOf(Table) < 0 Then _Owner.AddReferencedFunction(Table)


            Controls.Add(tmpJDtable)
            AddHandler tmpJDtable.JoinCreated, AddressOf OnJoinCreatedHandler
            AddHandler tmpJDtable.TableMoving, AddressOf OnTableMovingHandler
            AddHandler tmpJDtable.TableMoved, AddressOf OnTableMovedHandler
            RaiseEvent TableCreated(Me, New JoinDesignerTableEventArgs(tmpJDtable, Location))
            Return True
        Else
            Return False
        End If
    End Function

    ''' <summary>
    ''' Remove all Tables
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Clear()
        If Not _Owner Is Nothing AndAlso Not _Owner.References Is Nothing Then
            For i As Integer = _Owner.References.Count - 1 To 0 Step -1
                RemoveTable(i)
            Next
        End If

    End Sub

    ''' <summary>
    ''' Remove Table
    ''' </summary>
    ''' <param name="Table">Table to be removed</param>
    ''' <remarks></remarks>
    Sub RemoveTable(ByVal Table As DAM.Function.DAMfunction)

        Dim tmpJDtable As JoinDesignerTable = Me.TableByObject(Table)
        If Not tmpJDtable Is Nothing Then
            For Each item As DAM.Function.Field.FunctionField In Table.Fields
                For j As Integer = _Owner.Joins.Count - 1 To 0 Step -1
                    If item.Equals(_Owner.Joins(j).LeftField) Or item.Equals(_Owner.Joins(j).RightField) Then
                        Me.RemoveJoin(_Owner.Joins(j))
                    End If
                Next
            Next

            _Owner.References.Remove(Table)

            Controls.Remove(tmpJDtable)
            RaiseEvent TableRemoved(Me, New JoinDesignerTableEventArgs(tmpJDtable, tmpJDtable.Location))
            Me.Refresh()
        End If

    End Sub

    ''' <summary>
    ''' Remove Table
    ''' </summary>
    ''' <param name="i">Index of the Table to be removed</param>
    ''' <remarks></remarks>
    Sub RemoveTable(ByVal i As Integer)
        RemoveTable(_Owner.References(i))
    End Sub

    Sub AddJoin(ByVal LeftField As DAM.Function.Field.FunctionField, ByVal RightField As DAM.Function.Field.FunctionField)
        AddJoin(LeftField, RightField, DAM.Function.Join.JoinType.InnerJoin)
    End Sub

    Sub AddJoin(ByVal LeftField As DAM.Function.Field.FunctionField, ByVal RightField As DAM.Function.Field.FunctionField, ByVal JoinType As DAM.Function.Join.JoinType)
        OnJoinCreatedHandler(Me, New JoinDesignerJoinEventArgs(LeftField, RightField, JoinType))
    End Sub

    Sub RemoveJoin(ByVal Join As DAM.Function.Join.Join)
        _Owner.Joins.Remove(Join)
    End Sub

    Sub RemoveJoin(ByVal i As Integer)
        _Owner.Joins.RemoveAt(i)
    End Sub

    Public ReadOnly Property Joins() As DAM.Function.Join.JoinList
        Get
            Return _Owner.Joins
        End Get
    End Property

    Public ReadOnly Property Tables(ByVal i As Integer) As JoinDesignerTable
        Get
            Return Tables(_Owner.References(i))
        End Get
    End Property

    Public ReadOnly Property Tables(ByVal Table As DAM.Function.DAMfunction) As JoinDesignerTable
        Get
            Return TableByObject(Table)
        End Get
    End Property

    Public ReadOnly Property TableCount() As Integer
        Get
            Return _Owner.References.Count
        End Get
    End Property

    Public ReadOnly Property Joins(ByVal i As Integer) As DAM.Function.Join.Join
        Get
            Return _Owner.Joins(i)
        End Get
    End Property

    Public Property TableContextMenuStrip() As ContextMenuStrip
        Get
            Return _TableContextMenuStrip
        End Get
        Set(ByVal value As ContextMenuStrip)
            _TableContextMenuStrip = value
            For Each item As Control In Controls
                If TypeOf item Is JoinDesignerTable Then
                    DirectCast(item, JoinDesignerTable).ContextMenuStrip = value
                End If
            Next
        End Set
    End Property

    Private Enum JoinEndType
        RightArrow = 1
        LeftArrow = -1
        RightRect = 2
        LeftRect = -2
    End Enum

    Private Function DrawJoinEnd(ByVal Location As Drawing.Point, ByVal g As Drawing.Graphics, ByVal p As Pen, ByVal JoinEnd As JoinEndType) As Drawing.Point


        If JoinEnd = JoinEndType.LeftArrow Or JoinEnd = JoinEndType.LeftRect Then
            g.DrawLine(p, Location.X + 2, Location.Y + 7, Location.X + _JoinLineOfs, Location.Y + 7)
            If JoinEnd = JoinEndType.LeftArrow Then
                Dim LeftArrowTempl(4) As Drawing.Point
                LeftArrowTempl(0) = New Drawing.Point(Location.X, Location.Y + 7)
                LeftArrowTempl(1) = New Drawing.Point(Location.X + 5, Location.Y + 2)
                LeftArrowTempl(2) = New Drawing.Point(Location.X + 5, Location.Y + 12)
                LeftArrowTempl(3) = New Drawing.Point(Location.X, Location.Y + 7)
                g.FillPolygon(Brushes.Black, LeftArrowTempl)
            Else
                g.FillRectangle(Brushes.Black, New Drawing.Rectangle(Location.X, Location.Y + 5, 2, 5))
            End If
            Return New Drawing.Point(Location.X + _JoinLineOfs, Location.Y + 7)
        Else
            g.DrawLine(p, Location.X - 2, Location.Y + 7, Location.X - _JoinLineOfs, Location.Y + 7)
            If JoinEnd = JoinEndType.RightArrow Then
                Dim RightArrowTempl(4) As Drawing.Point
                RightArrowTempl(0) = New Drawing.Point(Location.X, Location.Y + 7)
                RightArrowTempl(1) = New Drawing.Point(Location.X - 5, Location.Y + 2)
                RightArrowTempl(2) = New Drawing.Point(Location.X - 5, Location.Y + 12)
                RightArrowTempl(3) = New Drawing.Point(Location.X, Location.Y + 7)
                g.FillPolygon(Brushes.Black, RightArrowTempl)
            Else
                g.FillRectangle(Brushes.Black, New Drawing.Rectangle(Location.X - 2, Location.Y + 5, 2, 5))
            End If
            Return New Drawing.Point(Location.X - _JoinLineOfs, Location.Y + 7)
        End If

    End Function


    Private Sub TableJoinDesigner_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

        If _Owner Is Nothing OrElse _Owner.Joins Is Nothing Then Exit Sub
        Me.SuspendLayout()

        Dim g As System.Drawing.Graphics = e.Graphics

        Dim p As New Pen(Color.Black)

        Dim tmpLeftPoint As Drawing.Point
        Dim tmpRightPoint As Drawing.Point
        For Each j As Core.Functions.MetisJoin In _Owner.Joins

            If j.Equals(_SelectedJoin) Then
                p.Width = 2
            Else
                p.Width = 1
            End If

            Dim tmpLeftTable As JoinDesignerTable = Me.TableByObject(j.LeftField.Function)
            Dim tmpRightTable As JoinDesignerTable = Me.TableByObject(j.RightField.Function)

            If tmpLeftTable.Location.X + tmpLeftTable.Size.Width / 2 < tmpRightTable.Location.X + tmpRightTable.Size.Width / 2 Then
                Select Case j.JoinType
                    Case DAM.Function.Join.JoinType.InnerJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Right, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.LeftRect)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Left, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.RightRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                    Case DAM.Function.Join.JoinType.LeftJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Right, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.LeftArrow)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Left, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.RightRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                    Case DAM.Function.Join.JoinType.RightJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Left, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.RightArrow)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Right, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.LeftRect)

                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                        'If p.Width > 1 Then g.DrawLine(p, tmpLeftPoint.X, tmpLeftPoint.Y + 1, tmpRightPoint.X, tmpRightPoint.Y + 1)
                End Select
            Else
                Select Case j.JoinType
                    Case Core.Functions.MetisJoinType.InnerJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Left, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.RightRect)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Right, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.LeftRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                    Case Core.Functions.MetisJoinType.LeftJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Left, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.RightArrow)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Right, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.LeftRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                    Case Core.Functions.MetisJoinType.RightJoin
                        tmpLeftPoint = DrawJoinEnd(New Drawing.Point(tmpRightTable.Right, tmpRightTable.FieldLocation(j.RightField).Y), g, p, JoinEndType.LeftArrow)
                        tmpRightPoint = DrawJoinEnd(New Drawing.Point(tmpLeftTable.Left, tmpLeftTable.FieldLocation(j.LeftField).Y), g, p, JoinEndType.RightRect)
                        g.DrawLine(p, tmpLeftPoint, tmpRightPoint)
                        'If p.Width > 1 Then g.DrawLine(p, tmpLeftPoint.X, tmpLeftPoint.Y + 1, tmpRightPoint.X, tmpRightPoint.Y + 1)
                End Select
            End If
        Next

        Me.ResumeLayout()

    End Sub


    Public Function JoinByLocation(ByVal p As Drawing.Point) As Core.Functions.MetisJoin
        Dim tmpLeftPoint As Drawing.Point
        Dim tmpRightPoint As Drawing.Point
        Dim tmpRelPos As New Drawing.Point

        Dim tmpLeft As Integer
        Dim tmpTop As Integer
        Dim tmpWidth As Integer
        Dim tmpHeight As Integer

        For Each j As DAM.Function.Join.Join In _Owner.Joins

            Dim tmpLeftTable As JoinDesignerTable = Me.TableByObject(j.LeftField.ParentFunction)
            Dim tmpRightTable As JoinDesignerTable = Me.TableByObject(j.RightField.ParentFunction)

            If tmpLeftTable.Location.X + tmpLeftTable.Size.Width / 2 < tmpRightTable.Location.X + tmpRightTable.Size.Width / 2 Then
                tmpLeftPoint.X = tmpLeftTable.Right + _JoinLineOfs
                tmpLeftPoint.Y = tmpLeftTable.FieldLocation(j.LeftField).Y + 7

                tmpRightPoint.X = tmpRightTable.Left - _JoinLineOfs
                tmpRightPoint.Y = tmpRightTable.FieldLocation(j.RightField).Y + 7

            Else
                tmpRightPoint.X = tmpLeftTable.Left - _JoinLineOfs
                tmpRightPoint.Y = tmpLeftTable.FieldLocation(j.LeftField).Y + 7

                tmpLeftPoint.X = tmpRightTable.Right + _JoinLineOfs
                tmpLeftPoint.Y = tmpRightTable.FieldLocation(j.RightField).Y + 7
            End If



            tmpLeft = Math.Min(tmpLeftPoint.X, tmpRightPoint.X)
            tmpTop = Math.Min(tmpLeftPoint.Y, tmpRightPoint.Y)
            tmpWidth = Math.Abs(tmpLeftPoint.X - tmpRightPoint.X)
            tmpHeight = Math.Abs(tmpLeftPoint.Y - tmpRightPoint.Y)

            If _ClickPos.X >= tmpLeft - 1 And _ClickPos.X <= tmpLeft + tmpWidth + 1 AndAlso _
               _ClickPos.Y >= tmpTop - 1 And _ClickPos.Y <= tmpTop + tmpHeight + 1 Then
                If (tmpLeftPoint.X - tmpRightPoint.X) * (tmpLeftPoint.Y - tmpRightPoint.Y) > 0 Then
                    If (_ClickPos.X - tmpLeft >= Math.Truncate(tmpWidth / tmpHeight * (_ClickPos.Y - tmpTop)) - 1 And _
                        _ClickPos.X - tmpLeft <= Math.Truncate(tmpWidth / tmpHeight * (_ClickPos.Y - tmpTop)) + 1) OrElse _
                       (_ClickPos.Y - tmpTop >= Math.Truncate(tmpHeight / tmpWidth * (_ClickPos.X - tmpLeft)) - 1 And _
                        _ClickPos.Y - tmpTop <= Math.Truncate(tmpHeight / tmpWidth * (_ClickPos.X - tmpLeft)) + 1) Then

                        Return j
                        Exit For
                    End If
                Else
                    If (_ClickPos.X - tmpLeft >= Math.Truncate(tmpWidth / tmpHeight * (tmpTop + tmpHeight - _ClickPos.Y)) - 1 And _
                        _ClickPos.X - tmpLeft <= Math.Truncate(tmpWidth / tmpHeight * (tmpTop + tmpHeight - _ClickPos.Y)) + 1) OrElse _
                       (_ClickPos.Y - tmpTop >= Math.Truncate(tmpHeight / tmpWidth * (tmpLeft + tmpWidth - _ClickPos.X)) - 1 And _
                        _ClickPos.Y - tmpTop <= Math.Truncate(tmpHeight / tmpWidth * (tmpLeft + tmpWidth - _ClickPos.X)) + 1) Then

                        Return j
                        Exit For
                    End If
                End If
            End If
        Next
        Return Nothing
    End Function

    Public Property SelectJoinOnClick() As Boolean
        Get
            Return _SelectJoinOnClick
        End Get
        Set(ByVal value As Boolean)
            _SelectJoinOnClick = value
            If Not _SelectJoinOnClick Then
                _SelectedJoin = Nothing
                Refresh()
            End If
        End Set
    End Property

    Public ReadOnly Property SelectedJoin() As DAM.Function.Join.Join
        Get
            Return _SelectedJoin
        End Get
    End Property


    Private Sub TableJoinDesigner_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Click
        Dim tmoJoin As DAM.Function.Join.Join = JoinByLocation(_ClickPos)
        If Not tmoJoin Is Nothing Then
            Dim tmpPos As New System.Drawing.Point(_ClickPos.X, _ClickPos.Y)
            tmpPos = PointToScreen(tmpPos)
            Dim tmoEventArgs As New System.Windows.Forms.MouseEventArgs(_ClickButton, 1, tmpPos.X, tmpPos.Y, 0)
            If _ClickButton = Windows.Forms.MouseButtons.Right Then
                RaiseEvent JoinRightClick(tmoJoin, tmoEventArgs)
            Else
                RaiseEvent JoinClick(tmoJoin, tmoEventArgs)
            End If
        End If

        If _SelectJoinOnClick Then
            _SelectedJoin = tmoJoin
            Me.Refresh()
        End If
    End Sub

    Private Sub TableJoinDesigner_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.DoubleClick
        Dim tmoJoin As DAM.Function.Join.Join = JoinByLocation(_ClickPos)
        If Not tmoJoin Is Nothing Then
            Dim tmoEventArgs As New System.Windows.Forms.MouseEventArgs(_ClickButton, 1, _ClickPos.X, _ClickPos.Y, 0)
            RaiseEvent JoinDoubleClick(tmoJoin, tmoEventArgs)
        End If
    End Sub

    Private Sub TableJoinDesigner_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
        _ClickPos = e.Location
        _ClickButton = e.Button
    End Sub
End Class


Public Class JoinDesignerTableEventArgs
    Inherits EventArgs

    Public _Position As System.Drawing.Point
    Public _Table As JoinDesignerTable


    Sub New(ByVal Table As JoinDesignerTable, ByVal Position As System.Drawing.Point)
        _Table = Table
        _Position = Position
    End Sub

    Public ReadOnly Property Position() As System.Drawing.Point
        Get
            Return _Position
        End Get
    End Property

    Public ReadOnly Property Table() As JoinDesignerTable
        Get
            Return _Table
        End Get
    End Property

End Class